/******************************************************************************
This file is part of AppKit.
Project: appkit
Author : FergusZeng
Email  : cblock@126.com
git	   : https://gitee.com/newgolo/appkit.git
*******************************************************************************
MIT License

Copyright (c) 2022 cblock@126.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
******************************************************************************/
#pragma once

#include <limits.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <unistd.h>

#include <atomic>
#include <condition_variable>
#include <future>
#include <memory>
#include <mutex>
#include <set>
#include <string>
#include <thread>
#include <utility>
#include <vector>

#include "appkit/basetype.h"
#include "appkit/singleton.h"
/**
 *  @file   thread.h
 *  @brief  线程
 */
namespace appkit {
class Thread;
/**
 *  @class  Runnable
 *  @brief  线程运行体,子类必须重写run方法.
 */
class Runnable {
    DECL_CLASSMETA(Runnable)
public:
    Runnable();
    virtual ~Runnable();
    bool isExited();
    /**
     * @brief 线程体主函数
     * @param thread 当前线程体依附的线程
     * @note 所有子类必须实现
     */
    virtual void run(const Thread& thread) = 0;

private:
    friend class Thread;
    Thread* m_thread{nullptr};
};

class Thread {
    DECL_CLASSMETA(Thread)

public:
    Thread();
    explicit Thread(const std::string& name);
    Thread(const Thread& copy);
    virtual ~Thread();
    /**
     * @brief 克隆函数
     * @return std::unique_ptr<Thread>
     */
    virtual std::unique_ptr<Thread> clone() const;
    /**
     *  @brief  启动线程
     *  @param  runnable Runnable对象
     *  @return 成功返回true,失败返回false
     */
    virtual bool start(const Runnable& runnable);
    /**
     *  @brief  停止线程
     *  @param  msTimeout 超时时间,<0时表示阻塞等待线程退出
     *  @return 成功返回true,失败返回false
     */
    virtual bool stop(int msTimeout = -1);
    /**
     *  @brief  判断线程是否正在运行
     *  @param  void
     *  @return 正在运行返回true,否则返回false
     */
    virtual bool isRunning() const;
    /**
     *  @brief  微秒延时函数
     *  @param  us 要延时的微秒数
     *  @return void
     *  @note   函数休眠时,当前进程可能会让出CPU,引发进程调度
     *          注意使用usleep时,时间不能设置得太短,否则调度时
     *          进程间切换太频繁非常耗资源!!!推荐最小值为100us
     */
    static void usleep(int us);
    /**
     *  @brief  毫秒延时函数
     *  @param  ms 要延时的毫秒数
     *  @return void
     *  @note   函数休眠时,当前进程可能会让出CPU,引发进程调度
     */
    static void msleep(int ms);
    /**
     * @brief 获取当前代码所在线程ID
     * @return int
     */
    static int threadID();

    /**
     * @brief 设置线程CPU亲和性
     * @param cpuId CPU序号(0~N)
     * @return bool
     * @note 设置进程CPU亲和性请使用System::setAffinity()
     */
    static bool setAffinity(int cpuId);

    /**
     * @brief 设置当前线程名称
     * @param name
     */
    static void setName(const std::string& name);

protected:
    Runnable* m_runnable{nullptr};
    std::string m_name{""};

private:
    std::future<void> m_future;
    std::atomic<bool> m_running{false};
};

/**
 *  @class PThread
 *  @brief 线程类
 */
class PThread : public Thread {
    DECL_CLASSMETA(PThread)

public:
    /**
     * @brief 线程属性说明
     * 调度策略
     * SCHED_OTHER:普通调度策略,优先级只能设置为0
     * SCHED_FIFO:不同优先级抢占,同等优先级先进先出,优先级可以设置为1(低)~99(高)
     * SCHED_RR:不同优先级抢占,同等优先级均分时间片,优先级可以设置为1(低)~99(高)
     * 继承方式
     * PTHREAD_INHERIT_SCHED(继承自父线程,忽略当前设置的属性),PTHREAD_EXPLICIT_SCHED(采用当前设置的线程属性)
     * 栈大小
     * 最小值为PTHREAD_STACK_MIN(16384)
     */
    enum SCHED_POLICY_E {
        SCHED_POLICY_OTHER = SCHED_OTHER,
        SCHED_POLICY_FIFO = SCHED_FIFO,
        SCHED_POLICY_RR = SCHED_RR,
    };
    struct ThreadAttr {
        int m_policy{SCHED_POLICY_OTHER};
        int m_priority{0};
        int m_inherit{PTHREAD_INHERIT_SCHED};
        int m_stackSize{PTHREAD_STACK_MIN};
    };
    enum THREAD_STATE {
        STATE_INIT = 0,
        STATE_START,
        STATE_RUNNING,
        STATE_EXIT
    };

public:
    PThread();
    PThread(int policy, int priority, bool inherit = false, int stackSize = 0);
    virtual ~PThread();
    std::unique_ptr<Thread> clone() const override;
    /**
     *  @brief  启动线程
     *  @param  runnable Runnable对象
     *  @return 成功返回true,失败返回false
     */
    bool start(const Runnable& runnable) override;
    /**
     *  @brief  停止线程
     *  @param  msTimeout 超时时间,<0时表示阻塞等待线程退出
     *  @return 成功返回true,失败返回false
     */
    bool stop(int msTimeout = -1) override;
    /**
     *  @brief  判断线程是否正在运行
     *  @param  void
     *  @return 正在运行返回true,否则返回false
     */
    bool isRunning() const override;
    /**
     *  @brief  微秒延时函数
     *  @param  us 要延时的微秒数
     *  @return void
     *  @note   函数休眠时,当前进程可能会让出CPU,引发进程调度
     *          注意使用usleep时,时间不能设置得太短,否则调度时
     *          进程间切换太频繁非常耗资源!!!推荐最小值为100us
     */
    static void usleep(int us);
    /**
     *  @brief  毫秒延时函数
     *  @param  ms 要延时的毫秒数
     *  @return void
     *  @note   函数休眠时,当前进程可能会让出CPU,引发进程调度
     */
    static void msleep(int ms);

private:
    bool setAttribute(const pthread_attr_t& pAttr);
    void threadMain();
    static void* startRoutine(void* arg);

private:
    bool m_runFlag{false};
    int m_status{STATE_INIT};
    pthread_t m_threadID{0};
    pthread_attr_t m_attribute;
    ThreadAttr m_threadAttribute;
};

/**
 *  @class Threading
 *  @brief 线程化类,用以线程化类成员函数
 *  @note 用法
    class MultiThread:public Threading{
    public:
    MultiThread()
    {
        char* args = "hello";
        threading(&MultiThread::threadA,this);
        threading(&MultiThread::threadB,this,args);
    }
    private:
    void threadA()
    {
    }
    void threadB(void* args)
    {
    }
    };
 */
class Threading {
    DECL_CLASSMETA(Threading)

public:
    Threading();
    virtual ~Threading();
    /**
     * @brief 创建Thread线程
     * @tparam Function
     * @tparam Args
     * @param f
     * @param args
     * @return bool
     * @note 该接口有内存泄露的风险!!!
     */
    template <typename Function, typename... Args>
    bool threading(Function&& f, Args&&... args) {
        auto future = std::async(std::launch::async, std::bind(f, args...));
        m_futures.push_back(std::move(future));
        return true;
    }
    /**
     * @brief 创建PThread线程
     * @tparam Function
     * @tparam Args
     * @param f
     * @param args
     * @return true
     * @return false
     */
    template <typename Function, typename... Args>
    bool pthreading(Function&& f, Args&&... args) {
        return startPThreading(std::bind(f, args...));
    }

private:
    bool startPThreading(std::function<void(void*)> func);
    static void* startRoutine(void* arg);

private:
    std::vector<std::future<void>> m_futures;
};

/**
 * @class RWMutex
 * @brief 读写锁
 */
class RWMutex {
public:
    void lockR();
    void unLockR();
    void lockW();
    void unLockW();

private:
    std::mutex m_rMutex;
    std::mutex m_wMutex;
    int m_readCnts{0};  // 已加读锁个数
};

/**
 *  @class Semaphore
 *  @brief 信号量类(基于POSIX接口)
 *  @note 无名信号量用于线程间通信,有名信号量用于进程间通信
 *        !!!有名信号量目前测试有问题,请使用SemaphoreV替代!!!
 */
class Semaphore {
    DECL_CLASSMETA(Semaphore)

public:
    Semaphore();
    ~Semaphore();
    /**
     *  @brief  打开信号量
     *  @param name 名称中不能包含'/',name为空时表示创建无名信号量
     *  @param  value 打开有名信号量时,如果信号量已经存在,该值会被忽略
     *  @return 成功返回true,失败返回false
     *  @note   信号量存在,则返回,不存在则创建
     */
    virtual bool open(const std::string& name, int value);
    /**
     *  @brief  关闭信号量
     *  @param  void
     *  @return 成功返回true,失败返回false
     *  @note   对于有名信号量,关闭信号量不会真正删除信号量,还需要调用unlink()
     */
    virtual bool close();
    /**
     *  @brief  删除信号量
     *  @param  void
     *  @return 成功返回true,失败返回false
     *  @note   该方法仅对有名信号量有效
     */
    virtual bool unlink();
    /**
     *  @brief  等待信号量
     *  @param  msTimeout 超时时间
     *  @return 成功返回true,失败返回false
     *  @note   使信号量值减1,如果无资源可申请,则阻塞.
     */
    virtual bool wait(int msTimeout = -1);
    /**
     *  @brief  尝试等待信号量
     *  @param  void
     *  @return 成功返回true,失败返回false
     *  @note   使信号量值减1,如果无资源可申请,则返回.
     */
    virtual bool tryWait();
    /**
     *  @brief  释放信号量
     *  @param  void
     *  @return 成功返回true,失败返回false
     *  @note   使信号量值增加1,释放资源
     */
    virtual bool post();
    /**
     *  @brief  获取信号量值
     *  @param  value 当前值
     *  @return 成功返回true,失败返回false
     *  @note   当value>0说明有资源,=0无资源,<0表示有|value|个线程在等待资源
     */
    virtual bool getValue(int* value);

protected:
    sem_t m_sem;
    std::string m_name;
};

/**
 *  @class SemaphoreV
 *  @brief 信号量类(基于systemV接口)
 *  @note  使用systemV实现的进程间信号量
 */
class SemaphoreV : public Semaphore {
    DECL_CLASSMETA(SemaphoreV)

public:
    SemaphoreV();
    ~SemaphoreV();
    /**
     * @brief 打开信号量
     * @param name 信号量名称(一般为一个文件名)
     * @param value 初始值(创建时初始化),当信号量已经存在时忽略该值
     * @return true
     * @return false
     */
    bool open(const std::string& name, int value) final;
    /**
     * @brief 关闭信号量
     */
    bool close() final;
    /**
     * @brief 等待信号量
     * @param msTimeout 超时时间
     * @return true
     * @return false
     */
    bool wait(int msTimeout = -1) final;
    /**
     * @brief 发送信号量
     * @return true
     * @return false
     */
    bool post() final;
    /**
     * @brief 获取信号量值
     * @param value
     * @return true
     * @return false
     */
    bool getValue(int* value) final;

private:
    int m_semid{-1};
};

/**
 * @brief 事件队列
 */
class EventQueue {
    DECL_CLASSMETA(EventQueue)
    using Ptr = std::shared_ptr<EventQueue>;

public:
    EventQueue();
    virtual ~EventQueue();
    void clearEvent();
    /**
     * @brief 通知事件到达
     * @param event 该值要大于等于0
     * @return bool 成功/失败
     */
    bool notifyEvent(const int& event);

    /**
     * @brief 等待事件
     * @param eventSet 要等待的事件集
     * @param msTimeout 超时时间
     * @return int 返回事件值,如果小于0代表错误或者超时
     */
    int waitEvent(const std::set<int>& eventSet, uint32 msTimeout);

private:
    std::vector<int> m_eventQueue;
    std::condition_variable m_cond;
    std::mutex m_cvMutex;
    std::atomic<bool> m_isWaiting{false};
};

}  // namespace appkit
